using System;
using System.Collections.Generic;
using System.Diagnostics;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using Debug = UnityEngine.Debug;

/// <summary>
/// 射线批处理调度器：收集射线请求 -> 批量下发 -> 跨帧回收 -> 主线程回调
/// - 默认：下一帧回调（非阻塞）
/// - 可选：同帧完成（阻塞，实时）
/// - 限流：maxRaysPerFrame
/// - 调优：batchSize（RaycastCommand.ScheduleBatch 的 minCommandsPerJob）
/// 用法：RaycastBatchManager.Ensure().RequestRaycast(...);
/// </summary>
public sealed class RaycastBatchManager : MonoBehaviour
{
    public static RaycastBatchManager Instance { get; private set; }

    [Header("性能设置")]
    [Tooltip("每帧最多处理多少条射线（限流防卡顿）")]
    public int maxRaysPerFrame = 100;

    [Tooltip("RaycastCommand.ScheduleBatch 的 minCommandsPerJob，建议 32~128")]
    public int batchSize = 64;

    [Tooltip("是否强制同帧完成（会阻塞主线程）；false=下一帧回调更平滑")]
    public bool forceCompleteThisFrame = false;

    [Tooltip("是否输出耗时日志")]
    public bool logTiming = false;

    // —— 请求结构 —— //
    private struct RaycastRequest
    {
        public Vector3 origin;
        public Vector3 direction; // 期望已归一化
        public float distance;
        public int layerMask;
        public Action<RaycastHit> callback;
    }

    private readonly List<RaycastRequest> requestQueue = new();

    // —— 在途批次（跨帧保存） —— //
    private struct InFlightBatch
    {
        public bool valid;
        public NativeArray<RaycastCommand> commands;
        public NativeArray<RaycastHit> results;
        public List<Action<RaycastHit>> callbacks;
        public JobHandle handle;

        public void Dispose()
        {
            if (commands.IsCreated) commands.Dispose();
            if (results.IsCreated)  results.Dispose();
            callbacks = null;
            valid = false;
        }
    }
    private InFlightBatch _inFlight;

    // —— 生命周期 —— //
    void Awake()
    {
        if (Instance != null && Instance != this) { Destroy(this); return; }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }

    void OnDisable()
    {
        if (_inFlight.valid)
        {
            _inFlight.handle.Complete();
            _inFlight.Dispose();
        }
    }

    // —— 单例兜底 —— //
    public static RaycastBatchManager Ensure()
    {
        if (Instance != null) return Instance;
        var go = new GameObject("[RaycastBatchManager]");
        return (Instance = go.AddComponent<RaycastBatchManager>());
    }

    // —— 对外 API —— //
    /// <summary>
    /// 发起一条射线请求（默认下一帧回调；若 forceCompleteThisFrame=true 则当帧回调）
    /// </summary>
    public void RequestRaycast(Vector3 origin, Vector3 direction, float distance, int layerMask, Action<RaycastHit> callback)
    {
        if (direction.sqrMagnitude < 1e-10f) direction = Vector3.forward;
        direction.Normalize();
        if (distance < 0f) distance = 0f;

        requestQueue.Add(new RaycastRequest
        {
            origin = origin,
            direction = direction,
            distance = distance,
            layerMask = layerMask,
            callback = callback
        });
    }

    // —— 跨帧收尾（若在途批次已完成，则分发回调并释放） —— //
    private void FinalizeInFlightIfReady()
    {
        if (_inFlight.valid && _inFlight.handle.IsCompleted)
        {
            _inFlight.handle.Complete();
            DispatchResults(_inFlight.results, _inFlight.callbacks);
            _inFlight.Dispose();
        }
    }

    void Update()
    {
        FinalizeInFlightIfReady();
    }

    void LateUpdate()
    {
        // 先尝试收尾，避免覆盖未完成的在途批次
        FinalizeInFlightIfReady();
        if (_inFlight.valid) return; // 仍在跑：本帧不新开（自然限流）

        int totalRequests = requestQueue.Count;
        if (totalRequests == 0) return;

        int count = Mathf.Min(maxRaysPerFrame, totalRequests);
        if (count <= 0) return;

        var callbacks = new List<Action<RaycastHit>>(count);
        Stopwatch sw = null;
        if (logTiming) sw = Stopwatch.StartNew();

        if (forceCompleteThisFrame)
        {
            var commands = new NativeArray<RaycastCommand>(count, Allocator.TempJob);
            var results  = new NativeArray<RaycastHit>(count, Allocator.TempJob);
            try
            {
                for (int i = 0; i < count; i++)
                {
                    var req = requestQueue[i];
                    commands[i] = new RaycastCommand(req.origin, req.direction, req.distance, req.layerMask);
                    callbacks.Add(req.callback);
                }

                var handle = RaycastCommand.ScheduleBatch(commands, results, Mathf.Max(1, batchSize));
                handle.Complete(); // 同帧阻塞
                DispatchResults(results, callbacks);
            }
            finally
            {
                if (commands.IsCreated) commands.Dispose();
                if (results.IsCreated)  results.Dispose();
            }
        }
        else
        {
            var commands = new NativeArray<RaycastCommand>(count, Allocator.TempJob);
            var results  = new NativeArray<RaycastHit>(count, Allocator.TempJob);

            for (int i = 0; i < count; i++)
            {
                var req = requestQueue[i];
                commands[i] = new RaycastCommand(req.origin, req.direction, req.distance, req.layerMask);
                callbacks.Add(req.callback);
            }

            var handle = RaycastCommand.ScheduleBatch(commands, results, Mathf.Max(1, batchSize));

            _inFlight = new InFlightBatch
            {
                valid     = true,
                commands  = commands,
                results   = results,
                callbacks = callbacks,
                handle    = handle
            };
        }

        // 分帧清空已提交的请求
        requestQueue.RemoveRange(0, count);

        if (logTiming && sw != null)
        {
            sw.Stop();
            Debug.Log($"[RaycastBatchManager] 批量 {count} 条（batchSize={batchSize}, force={forceCompleteThisFrame}）耗时: {sw.ElapsedMilliseconds} ms");
        }
    }

    private static void DispatchResults(NativeArray<RaycastHit> results, List<Action<RaycastHit>> callbacks)
    {
        int n = results.Length;
        for (int i = 0; i < n; i++)
        {
            try
            {
                callbacks[i]?.Invoke(results[i]);
            }
            catch (Exception ex)
            {
                Debug.LogException(ex);
            }
        }
    }
}